﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.parser;
using System.Text.RegularExpressions;
using System.IO;
using iTextSharp.text;
using Microsoft.VisualBasic.FileIO;

namespace pdfebookcutter
{
    public partial class PageForm : Form
    {
        public FileInfo PdfFileInfo { get; protected set; }

        PdfReader reader;
        PdfReaderContentParser newParser;
        EditionOptions EditOptions = new EditionOptions();


        public PageForm()
        {
            InitializeComponent();
        }

        internal void LoadFile(string FileName)
        {
            PdfFileInfo = new FileInfo(FileName);
            reader = new PdfReader(FileName);

            newParser = new PdfReaderContentParser(reader);

            ReloadGrouping();
        }

        private void toolStripComboBox1_Click(object sender, EventArgs e)
        {

        }

        private void toolStripButtonReload_Click(object sender, EventArgs e)
        {
            ReloadGrouping();
        }

        private void ReloadGrouping()
        {
            if (PageEditors.Any(p => p.CropAreas.Any()))
            {
                if (MessageBox.Show(this, "Areas may get lost. Contrinue", "Reload", MessageBoxButtons.OKCancel) != System.Windows.Forms.DialogResult.OK)
                    return;
            }

            var FirstPageSize = PdfShowPage.ToRectangle(reader.GetPageSize(1));

            var ExistingPageAreas =
                PageEditors.SelectMany(p => p.PageNums.Select(num => new { Page = num, CropAreas = p.CropAreas })).ToLookup(p => p.Page, e => e.CropAreas);

            flowLayoutPanel1.SuspendLayout();
            flowLayoutPanel1.Controls.Clear();
            flowLayoutPanel1.Controls.AddRange(EnumerateGroups().Select(p => new PdfShowPage()
            {
                Reader = reader,
                Parser = newParser,
                EditOptions = EditOptions,
                PageNums = new HashSet<int>(p),
                AnyBox = PdfShowPage.Normalize(FirstPageSize)
            }).ToArray());
            

            foreach (var editor in PageEditors)
            {
                var AllAreas = editor.PageNums.SelectMany(i => ExistingPageAreas[i].SelectMany(c => c)).Distinct().Select(a => a.Clone() as PdfShowPage.CropArea);

                editor.CropAreas.AddRange(AllAreas);
            }

            MDIParent Parent = ParentForm as MDIParent;

            foreach (var editor in PageEditors)
                editor.SchedulePageSizeActions(Parent);

            Parent.ScheduleAction(() => { }, () => {
                flowLayoutPanel1.SuspendLayout();
                foreach (var editor in PageEditors)
                    editor.AdjustPageSize();
                flowLayoutPanel1.ResumeLayout();
            });


            foreach (var editor in PageEditors)
                editor.ScheduleActions(Parent);

            flowLayoutPanel1.ResumeLayout();
        }

        public IEnumerable<PdfShowPage> PageEditors
        {
            get
            {
                return flowLayoutPanel1.Controls.OfType<PdfShowPage>();
            }
        }

        public IEnumerable<int> EditPages
        {
            get
            {
                return PageEditors.SelectMany(p => p.PageNums).Distinct().OrderBy(i => i);
            }

        }

        public IEnumerable<int[]> EnumerateGroups()
        {
            var GroupText = toolStripComboBoxGroupingConstruct.Text;
            var PageCount = reader.NumberOfPages;

            if (string.IsNullOrWhiteSpace(GroupText) || GroupText == "none" || GroupText == "single")
                return Enumerable.Range(1, PageCount).Select(p => new int[] { p });


            var AllPageSet = new HashSet<int>(Enumerable.Range(1, PageCount));

            return GroupText.Split(';').Select(part => CreateGroup(part, AllPageSet));
        }

        private int[] CreateGroup(string part, HashSet<int> AllPageSet)
        {
            var set = ParseGroup(part, AllPageSet);
            AllPageSet.ExceptWith(set);
            return set;
        }

        private int[] ParseGroup(string part, HashSet<int> AllPageSet)
        {
            if (part == "even")
            {
                return AllPageSet.Where(i => i % 2 == 0).ToArray();
            }

            if (part == "odd")
            {
                return AllPageSet.Where(i => i % 2 == 1).ToArray();
            }

            if (part == "other")
            {
                return AllPageSet.ToArray();
            }

            var numbers = part.Split(',');
            HashSet<int> NumberSet = new HashSet<int>();

            foreach (var numberpart in numbers)
            {
                if (numberpart.Contains('-'))
                {
                    var m = Regex.Match(numberpart, "(\\d+)-(\\d+)");


                    for (int i = int.Parse(m.Groups[1].Value); i <= int.Parse(m.Groups[2].Value); i++)
                    {
                        NumberSet.Add(i);
                    }
                }
                else
                    if (part == "odd")
                    {
                        NumberSet.RemoveWhere(i => i % 2 == 1);
                    }
                    else
                        if (part == "even")
                        {
                            NumberSet.RemoveWhere(i => i % 2 == 0);
                        }
                        else
                        {
                            NumberSet.Add(int.Parse(numberpart));
                        }

            }

            return NumberSet.ToArray();
        }

        internal void SaveAs(string newFilename)
        {
            if (File.Exists(newFilename))
            {
                try
                {
                    FileSystem.DeleteFile(newFilename, UIOption.AllDialogs, RecycleOption.SendToRecycleBin, UICancelOption.ThrowException);
                }
                catch (Exception)
                {
                    return;

                }

            }

            DoCropping(newFilename);
        }

        private void DoCropping(string FileName)
        {
            using (var output = new FileStream(FileName, FileMode.CreateNew, FileAccess.Write))
            {
                // Rectangle pageSize = reader.GetPageSize(1);


                Document document = new Document();


                PdfSmartCopy pdfCopy = new PdfSmartCopy(document, output);

                document.Open();

                foreach (var p in EditPages)
                {
                    var Editor = PageEditors.Where(e => e.PageNums.Contains(p)).FirstOrDefault();

                    if (Editor == null)
                        continue;

                    var Page = reader.GetPageN(p);


                    foreach (var c in Editor.CropAreas)
                    {

                        // overwrite properties, 
                        // pdf smart copy will create a copy 
                        var newRect = c.PdfValueArray;

                        var oldCropBox = Page.Get(PdfName.CROPBOX);
                        var oldMediaBox = Page.Get(PdfName.MEDIABOX);

                        Page.Put(PdfName.CROPBOX, newRect);
                        Page.Put(PdfName.MEDIABOX, newRect);

                        //var Page = reader.GetPageN(p);
                        var IndirectPage = pdfCopy.GetImportedPage(reader, p);

                        pdfCopy.AddPage(IndirectPage);

                        Page.Put(PdfName.CROPBOX, oldCropBox);
                        Page.Put(PdfName.MEDIABOX, oldMediaBox);

                    }



                }

                document.Close();

            }
        }

        private void toolStripButtonRemoveAll_Click(object sender, EventArgs e)
        {
            foreach (var p in PageEditors)
                p.RemoveAllAreas();
        }

        private void toolStripButtonKeepRatio34_Click(object sender, EventArgs e)
        {
            EditOptions.KeepRatio = 3.0f / 4.0f;
            UpdateRatioCheck();
        }

        private void toolStripButtonKeepRatio43_Click(object sender, EventArgs e)
        {
            EditOptions.KeepRatio = 4.0f / 3.0f;
            UpdateRatioCheck();
        }

        private void toolStripButtonKeepRatioNone_Click(object sender, EventArgs e)
        {
            EditOptions.KeepRatio = null;
            UpdateRatioCheck();
        }


        private void toolStripButtonAutoSpring_Click(object sender, EventArgs e)
        {
            EditOptions.AutoSpring = !EditOptions.AutoSpring;
            UpdateRatioCheck();
        }

        private void UpdateRatioCheck()
        {
            toolStripButtonKeepRatioNone.Checked = EditOptions.KeepRatio == null;
            toolStripButtonKeepRatio34.Checked = EditOptions.KeepRatio == 3.0f / 4.0f;
            toolStripButtonKeepRatio43.Checked = EditOptions.KeepRatio == 4.0f / 3.0f;
            toolStripButtonAutoSpring.Checked = EditOptions.AutoSpring;
        }

        private void PageForm_Load(object sender, EventArgs e)
        {

        }

        private void removeToolStripMenuItem_Click(object sender, EventArgs e)
        {


        }



   



    }


    public class EditionOptions
    {
        public float? KeepRatio { get; set; }


        public bool AutoSpring { get; set; }
    }



    //public class CroppedWriter : PdfSmartCopy
    //{
    //    private Document doc;
    //    private FileStream output;

    //         public CroppedWriter(Document doc, FileStream output)
    //             : base(doc, output)
    //    {
    //        // TODO: Complete member initialization
    //        this.doc = doc;
    //        this.output = output;
    //    }

    //         public void AddCroppedPage(PdfImportedPage iPage, RectangleF c)
    //         {
    //             int pageNum = SetFromIPage(iPage);

    //             PdfDictionary thePage = reader.GetPageN(pageNum);
    //             PRIndirectReference origRef = reader.GetPageOrigRef(pageNum);
    //             reader.ReleasePage(pageNum);
    //             //RefKey key = new RefKey(origRef);
    //             //PdfIndirectReference pageRef;
    //             //IndirectReferences iRef;
    //             //indirects.TryGetValue(key, out iRef);
    //             //if (iRef != null && !iRef.Copied)
    //             //{
    //             //    pageReferences.Add(iRef.Ref);
    //             //    iRef.SetCopied();
    //             //}
    //             //pageRef = CurrentPage;
    //             //if (iRef == null)
    //             //{
    //             //    iRef = new IndirectReferences(pageRef);
    //             //    indirects[key] = iRef;
    //             //}
    //             //iRef.SetCopied();

    //             AddPage(PdfShowPage.ToPdfRectangle(c), 0);

    //             PdfIndirectReference current = GetPageReference(CurrentPageNumber); 



    //             PdfDictionary newPage = CopyDictionary(thePage);

    //             AddToBody(newPage);

    //             //root.AddPage(newPage);
    //             //iPage.SetCopied();
    //             //++currentPageNumber;
    //         }

    //         protected PdfDictionary CopyDictionary(PdfDictionary inp)
    //         {
    //             PdfDictionary outp = new PdfDictionary();
    //             PdfObject type = PdfReader.GetPdfObjectRelease(inp.Get(PdfName.TYPE));

    //             foreach (PdfName key in inp.Keys)
    //             {
    //                 PdfObject value = inp.Get(key);
    //                 if (type != null && PdfName.PAGE.Equals(type))
    //                 {
    //                     if (!key.Equals(PdfName.B) && !key.Equals(PdfName.PARENT))
    //                         outp.Put(key, CopyObject(value));
    //                 }
    //                 else
    //                     outp.Put(key, CopyObject(value));
    //             }
    //             return outp;

    //         }
    //}
}
